﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using TradeSea;

namespace TradeSea
{
    class Ocean
    {
        Model theMesh;
        Model theOceanMesh;

        Texture2D diffuseOceanTexture;
        Texture2D normalOceanTexture;

        Vector3 offset;
        Vector3 offsetInt;

        // The object will contain the shader
        Effect effect;

        Vector4 ambientLightColor;
        Vector3 eyePos = new Vector3(0, 20, -100);
        Vector3 boatPosition = new Vector3(0, 0, 0);
        float totalTime = 0.0f;

        float oceanScale = 10;
        double rotateObjects = -2000.0f;

        // Parameters for Ocean shader
        EffectParameter projectionOceanParameter;
        EffectParameter viewOceanParameter;
        EffectParameter worldOceanParameter;
        EffectParameter ambientIntensityOceanParameter;
        EffectParameter ambientColorOceanParameter;

        EffectParameter diffuseIntensityOceanParameter;
        EffectParameter diffuseColorOceanParameter;
        EffectParameter lightDirectionOceanParameter;

        EffectParameter eyePosOceanParameter;
        EffectParameter specularColorOceanParameter;

        EffectParameter colorMapTextureOceanParameter;
        EffectParameter normalMapTextureOceanParameter;
        EffectParameter totalTimeOceanParameter;

        EffectParameter posOffsetParameter; //offset due to boat movement

        GraphicsDevice GraphicsDevice;

        ContentManager content;

        public Ocean(ContentManager content, GraphicsDevice GraphicsDevice)
        {
            this.content = content;
            this.GraphicsDevice = GraphicsDevice;
        }

        public void SetupOceanShaderParameters()
        {
            // Bind the parameters with the shader.
            posOffsetParameter = effect.Parameters["Offset"];

            worldOceanParameter = effect.Parameters["World"];
            viewOceanParameter = effect.Parameters["View"];
            projectionOceanParameter = effect.Parameters["Projection"];

            ambientColorOceanParameter = effect.Parameters["AmbientColor"];
            ambientIntensityOceanParameter = effect.Parameters["AmbientIntensity"];

            diffuseColorOceanParameter = effect.Parameters["DiffuseColor"];
            diffuseIntensityOceanParameter = effect.Parameters["DiffuseIntensity"];
            lightDirectionOceanParameter = effect.Parameters["LightDirection"];

            eyePosOceanParameter = effect.Parameters["EyePosition"];
            specularColorOceanParameter = effect.Parameters["SpecularColor"];

            colorMapTextureOceanParameter = effect.Parameters["ColorMap"];
            normalMapTextureOceanParameter = effect.Parameters["NormalMap"];
            totalTimeOceanParameter = effect.Parameters["TotalTime"];
        }

        public float getOceanHeightAtPoint(Vector3 worldSpaceLocation)
        {
            

            worldSpaceLocation.X += 64 * oceanScale; //Transformation move worldspace location onto ocean space
            worldSpaceLocation.Z -= 64 * oceanScale;
            worldSpaceLocation = worldSpaceLocation / oceanScale;

            //input.Position.z += sin((TotalTime / 3) + (-input.Position.y + -input.Position.x)) + sin((TotalTime / 2) + (input.Position.y * input.Position.y + input.Position.x) / 4);
            return (float) (Math.Sin((totalTime) + (worldSpaceLocation.X + worldSpaceLocation.Z)) + Math.Sin((totalTime) + (-worldSpaceLocation.X + 0.5 * -worldSpaceLocation.Z))); // + Math.Sin((totalTime / 2) + (worldSpaceLocation.X * worldSpaceLocation.X - worldSpaceLocation.Z) / 4));
            //return 0.1f * (float) Math.Sin((totalTime / 4) - worldSpaceLocation.X) * -worldSpaceLocation.X / 4; 
        
        }


        public void LoadOcean(GraphicsDeviceManager graphics, Model ocean, Effect _effect)
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            this.GraphicsDevice = graphics.GraphicsDevice;
            boatPosition = new Vector3(0, 0, 0);
            //theMesh = content.Load<Model>("fy_faen_ass");
            theOceanMesh = ocean;

            // Load the shader
            //effect = content.Load<Effect>("Shader");
            effect = _effect;

            // Set up the parameters
            SetupOceanShaderParameters();

            diffuseOceanTexture = content.Load<Texture2D>("water");
            normalOceanTexture = content.Load<Texture2D>("wavesbump");

        }

        public void UpdateOcean(GameTime gameTime, Vector3 location)
        {
            ambientLightColor = Color.White.ToVector4();

            KeyboardState keyState = Keyboard.GetState();
            if (keyState.IsKeyDown(Keys.W))
            {
                eyePos += new Vector3(0, 0, 2);
                boatPosition += new Vector3(0, 0, 2);
            }
            if (keyState.IsKeyDown(Keys.S))
            {
                eyePos -= new Vector3(0, 0, 2);
                boatPosition -= new Vector3(0, 0, 2);
            }
            if (keyState.IsKeyDown(Keys.A))
            {
                //eyePos -= new Vector3(10, 0, 0);

                eyePos += new Vector3((float)Math.Sin(20), 0, (float)Math.Cos(20));
            }
            if (keyState.IsKeyDown(Keys.D))
            {
                //eyePos += new Vector3(10, 0, 0);
                eyePos -= new Vector3((float)Math.Sin(20), 0, (float)Math.Cos(20));
            }
            //this.offset = new Vector3((int) offset.X, (int) offset.Y, (int) offset.Z);


            //this.offsetInt = new Vector3((int)(offset.X / oceanScale), (int)(offset.Y / oceanScale), (int)(offset.Z / oceanScale));
            this.offsetInt.X = (int)(location.X / oceanScale);
            this.offsetInt.Y = (int)(location.Y / oceanScale);
            this.offsetInt.Z = (int)(location.Z / oceanScale);

            this.offset.X = -64 * oceanScale; //this offset centres the ocean in the world origin
            this.offset.Z = 64 * oceanScale;

            rotateObjects += gameTime.ElapsedGameTime.Milliseconds / 10000.0;
           totalTime += gameTime.ElapsedGameTime.Milliseconds / 1000.0f;
            //eyePos = new Vector3(1000.0f * (float)Math.Sin(rotateObjects), 50.0f, 1000.0f * (float)Math.Cos(rotateObjects));
            //boatPosition += new Vector3(0, 0, 30);
            

            //rotateIslandMatrix = Matrix.CreateRotationY((float)0);
        }

        public void DrawOcean(GameTime gameTime, Matrix view, Matrix projection, GraphicsDeviceManager graphics)
        {

            ModelMesh mesh = theOceanMesh.Meshes[0];
            ModelMeshPart meshPart = mesh.MeshParts[0];

            // Set parameters
            posOffsetParameter.SetValue(offsetInt);

            projectionOceanParameter.SetValue(projection);
            viewOceanParameter.SetValue(view);
            worldOceanParameter.SetValue(Matrix.CreateRotationX(-MathHelper.PiOver2)  
                * Matrix.CreateRotationY(MathHelper.PiOver2)
                * Matrix.CreateScale(oceanScale, 1, oceanScale)
                * Matrix.CreateTranslation(offset));
            
            ambientIntensityOceanParameter.SetValue(0.4f);
            ambientColorOceanParameter.SetValue(ambientLightColor);
            diffuseColorOceanParameter.SetValue(new Vector4(1.0f, 1.0f, 1.0f, 0.4f));
            diffuseIntensityOceanParameter.SetValue(0.2f);
            specularColorOceanParameter.SetValue(Color.White.ToVector4());
            eyePosOceanParameter.SetValue(eyePos);
            colorMapTextureOceanParameter.SetValue(diffuseOceanTexture);
            normalMapTextureOceanParameter.SetValue(normalOceanTexture);
            totalTimeOceanParameter.SetValue(totalTime);

            Vector3 lightDirection = new Vector3(1.0f, 0.0f, -1.0f);

            //ensure the light direction is normalized, or
            //the shader will give some weird results
            lightDirection.Normalize();
            lightDirectionOceanParameter.SetValue(lightDirection);

            //set the vertex source to the mesh's vertex buffer
            this.GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer, meshPart.VertexOffset);

            //set the current index buffer to the sample mesh's index buffer
            this.GraphicsDevice.Indices = meshPart.IndexBuffer;

            this.GraphicsDevice.DepthStencilState = DepthStencilState.Default;

            effect.CurrentTechnique = effect.Techniques["Technique1"];

            for (int i = 0; i < effect.CurrentTechnique.Passes.Count; i++)
            {
                //EffectPass.Apply will update the device to
                //begin using the state information defined in the current pass
                effect.CurrentTechnique.Passes[i].Apply();

                //theMesh contains all of the information required to draw
                //the current mesh

                this.GraphicsDevice.DrawIndexedPrimitives(
                    PrimitiveType.TriangleList, 0, 0,
                    meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount);

            }
        }
    }
}
